package lxs.swift.instance;


import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Chain {
    private final List<Handle> handles = new ArrayList<>();

    private static final Pattern pattern = Pattern.compile("(\\w+)|(\\[(\\d+)\\])");


    public static Chain parse(String chainStr) {
        Chain chain = new Chain();
        Matcher matcher = pattern.matcher(chainStr);
        while (matcher.find()) {
            if (matcher.group(1) != null) {
                // 字符串键
                chain.key(matcher.group(1));
            } else if (matcher.group(3) != null) {
                // 数字索引
                chain.index(Integer.parseInt(matcher.group(3)));
            }
        }
        return chain;
    }

    private Chain() {
    }

    public Chain key(String key) {
        this.handles.add(new ByKey(key));
        return this;
    }

    public Chain index(int index) {
        this.handles.add(new ByIndex(index));
        return this;
    }

    public Object read(StructInstance<?> instance) {
        Object re = instance;
        for (Handle handle : handles) {
            re = handle.read(re);
            if (re == null) return null;
        }
        return re;
    }

    public void write(StructInstance<?> instance, Object value) {
        Object target = instance;
        for (int i = 0; i < handles.size(); i++) {
            Handle handle = handles.get(i);
            if (i == handles.size() - 1) {
                handle.write(target, value);
                return;
            }
            Object read = handle.read(target);
            if (read == null) {
                handle.fill(target);
                read = handle.read(target);
            }
            target = read;
        }


    }

    protected interface Handle {
        Object read(Object target);

        void write(Object target, Object value);

        void fill(Object target);
    }

    protected static class ByKey implements Handle {
        public final String key;
        private final Function<Object, Object> read;
        private final BiConsumer<Object, Object> write;
        private final Consumer<Object> fill;

        protected ByKey(String key) {
            Objects.requireNonNull(key);
            this.key = key;
            this.read = (target) -> {
                if (target == null) throw new RuntimeException("cannot read value from null by key '" + key + "'");
                if (target instanceof HashStruct) {
                    HashStruct struct = (HashStruct) target;
                    return struct.of(key);
                }
                throw new RuntimeException("can only read value by key from HashStruct");
            };
            this.write = (target, value) -> {
                if (target == null) throw new RuntimeException("cannot write value from null by key '" + key + "'");
                if (target instanceof HashStruct) {
                    HashStruct struct = (HashStruct) target;
                    struct.set(key, value);
                    return;
                }
                throw new RuntimeException("cannot write value by key '" + key + "' from " + target.getClass().getName());
            };
            this.fill = target -> this.write.accept(target, new HashStruct());
        }


        @Override
        public Object read(Object target) {
            return this.read.apply(target);
        }

        @Override
        public void write(Object target, Object value) {
            this.write.accept(target, value);
        }

        @Override
        public void fill(Object target) {
            this.fill.accept(target);
        }
    }

    protected static class ByIndex implements Handle {
        public final int index;
        private final Function<Object, Object> read;
        private final BiConsumer<Object, Object> write;
        private final Consumer<Object> fill;

        protected ByIndex(int index) {
            if (index < 0) throw new IllegalArgumentException("the index cannot be less than 0");
            this.index = index;
            this.read = (target) -> {
                if (target == null) throw new RuntimeException("cannot read value from null by index " + index);
                if (target instanceof StructArray) {
                    StructArray array = (StructArray) target;
                    return array.of(index);
                }
                throw new RuntimeException("can only read value by key from StructArray");
            };
            this.write = (target, value) -> {
                if (target == null) throw new RuntimeException("cannot write value from null by index " + index);
                if (target instanceof StructArray) {
                    StructArray array = (StructArray) target;
                    array.set(index, value);
                    return;
                }
                throw new RuntimeException("cannot write value by index " + index + " from " + target.getClass().getName());

            };
            this.fill = target -> this.write.accept(target, new StructArray());
        }

        @Override
        public Object read(Object target) {
            return this.read.apply(target);
        }

        @Override
        public void write(Object target, Object value) {
            this.write.accept(target, value);
        }

        @Override
        public void fill(Object target) {
            this.fill.accept(target);
        }
    }
}
